package com.theta.common.base.swagger.doc.config;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;
import com.github.xiaoymin.knife4j.aggre.core.pojo.SwaggerRoute;
import com.github.xiaoymin.knife4j.aggre.nacos.NacosInstance;
import com.github.xiaoymin.knife4j.aggre.nacos.NacosRoute;
import com.github.xiaoymin.knife4j.aggre.nacos.NacosService;
import com.github.xiaoymin.knife4j.aggre.repository.NacosRepository;
import com.github.xiaoymin.knife4j.aggre.spring.support.NacosSetting;
import com.google.common.collect.Lists;
import com.theta.common.base.swagger.doc.core.ThetaRouteDispatcher;
import com.theta.common.base.swagger.doc.utils.SpringContextUtils;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.thread.ThreadUtil;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;



@Setter
@Slf4j
public class NacosExtendRepository extends NacosRepository {

	private NacosSetting nacosSetting;
	private Map<String,NacosInstance> nacosInstanceMap=new HashMap<>();
	final ThreadPoolExecutor threadPoolExecutor=ThreadUtil.newExecutor(5,5);
	
	private SpringContextUtils springContextUtils;
	
	public NacosExtendRepository(NacosSetting nacosSetting) {
		super(nacosSetting);
		this.nacosSetting=nacosSetting;
        if (nacosSetting!=null&& CollectionUtil.isNotEmpty(nacosSetting.getRoutes())){
        	initNacosCfg(nacosSetting);
        	applyRoutesCfg(nacosSetting);
        }
	}
	
	public void initNacosCfg(NacosSetting nacosSetting){
        List<Future<Optional<NacosInstance>>> optionalList=new ArrayList<>();
        nacosSetting.getRoutes().forEach(nacosRoute -> optionalList.add(threadPoolExecutor.submit(new NacosService(nacosSetting.getServiceUrl(), nacosSetting.getSecret(), nacosRoute))));
        optionalList.stream().forEach(optionalFuture -> {
            try {
                Optional<NacosInstance> nacosInstanceOptional=optionalFuture.get();
                if (nacosInstanceOptional.isPresent()){
                    nacosInstanceMap.put(nacosInstanceOptional.get().getServiceName(),nacosInstanceOptional.get());
                }
            } catch (Exception e) {
                log.error("nacos get error:"+e.getMessage(),e);
            }
        });
    }
	
	private void applyRoutesCfg(NacosSetting nacosSetting) {
        if (CollectionUtil.isNotEmpty(nacosInstanceMap)){
            nacosSetting.getRoutes().forEach(nacosRoute -> {
                if (nacosRoute.getRouteAuth()==null||!nacosRoute.getRouteAuth().isEnable()){
                    nacosRoute.setRouteAuth(nacosSetting.getRouteAuth());
                }
                this.routeMap.put(nacosRoute.pkId(),new SwaggerRoute(nacosRoute,nacosInstanceMap.get(nacosRoute.getServiceName())));
            });
            nacosSetting.getRoutes().forEach(nacosRoute -> this.routeMap.put(nacosRoute.pkId(),new SwaggerRoute(nacosRoute,nacosInstanceMap.get(nacosRoute.getServiceName()))));
        }
    }
	
	public void reloadNacosRepository(NacosSetting nacosSetting) {
		initNacosCfg(nacosSetting);
    	applyRoutesCfg(nacosSetting);
		Map<String,NacosRoute> routeInfoMap= nacosSetting.getRoutes().stream().collect(Collectors.toMap(r->r.pkId(), r->r));
		try {
			List<String> deleteKeys=Lists.newArrayList();
			ThetaRouteDispatcher dispatcher =  SpringContextUtils.getBean(ThetaRouteDispatcher.class);
			this.routeMap.forEach((k,v)->{
				NacosRoute route=routeInfoMap.get(k);
				dispatcher.removeRouteCache(k);
				if(route==null) {
					log.info("{},{} not exist",k,v.getName());
					deleteKeys.add(k);
				}else {
					String url=new StringBuilder("http://").append(route.getServiceName()).toString();
					log.info("key:{},name:{},serviceName:{},uri:{}",k,v.getName(),route.getServiceName(),v.getUri(),url);
//					v.setUri(url);
				}
			});
			
			for(String key:deleteKeys) {
				this.routeMap.remove(key);
			}
			
			log.info("{}",dispatcher.toString());
		} catch (Exception e) {
			log.error("",e);
		}
	}
	
	public NacosSetting getNacosSetting() {
        return nacosSetting;
    }
	
	

}
